package org.restlesscode.javersion.transcoders.bean;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * Provides a mapping between classes and a technique for serialization for SVN storage.
 * Ideally, we'd like everything stored as readable strings so that objects can be 
 * browsed in an SVN repository just like any other file. 
 * 
 */
public class SerializationTable {

	protected Set<Class<?>> toStringClasses = new HashSet<Class<?>>();
	protected Set<Class<?>> toStringConstructorRegistered = new HashSet<Class<?>>();
	protected Map<Class<?>, StringSerializer<?>> registeredConvertibleClasses = new HashMap<Class<?>, StringSerializer<?>>();
	
	
	protected final Class<?>[] defaultToStringClasses = { String.class, 
			byte.class, short.class, int.class, long.class, float.class, double.class, 
			boolean.class, char.class, Character.class };
	
	protected final Class<?>[] defaultToStringConstructorClasses = {
			Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, 
			Boolean.class
	};

    /**
     * Method for storing a POJO field.
     */
	public enum StoreMethod {
        /**
         * Storage method for Java primitives.
         */
		TO_STRING,
        /**
         * Storage method where object can be constructed from toString() representation.
         */
		TO_STRING_CONSTRUCTOR,
        /**
         * Storage method with a registered converter.
         */
		REGISTERED,
        /**
         * Storage method using Java serialization.
         */
		SERIALIZE_OBJECT
	}
	
	protected SerializationTable() {
		toStringClasses.addAll(Arrays.asList(defaultToStringClasses));
		toStringConstructorRegistered.addAll(Arrays.asList(defaultToStringConstructorClasses));
	}
	
	public StoreMethod getStorageMethod(Class<?> c) {
		if (registeredConvertibleClasses.containsKey(c)) {
			return StoreMethod.REGISTERED;
		} else if (toStringConstructorRegistered.contains(c)) {
			return StoreMethod.TO_STRING_CONSTRUCTOR;
		} else if (toStringClasses.contains(c)) {
			return StoreMethod.TO_STRING;
		} else {
			return StoreMethod.SERIALIZE_OBJECT;
		}
	}
	
	protected <T> String serializeCustom(T o) {
		//return registeredConvertibleClasses.get(o.getClass()).serialize(o);
		
		StringSerializer<T> serializer = (StringSerializer<T>) registeredConvertibleClasses.get(o.getClass());
		return serializer.serialize(serializer.getSerializerSourceClass().cast(o));
	}
	
	protected <T> T deserializeCustom(String s, Class<T> c) {
		return (T) registeredConvertibleClasses.get(c).deserialize(s);
	}
	
	/**
	 * Register a class that meets the following condition: 
	 * It has a public constructor that takes the string generated by its toString()
	 * method and generates an equivalent object.
	 * 
	 * In other words:
	 *   Foo f;
	 *   new Foo(f.toString()) is equivalent to f
	 * 
	 * Examples in the JDK are the primitive wrappers such as Integer and Float.
	 * 
	 * @param c
	 */
	public void registerToStringConstructorClass(Class<?> c) {
		toStringConstructorRegistered.add(c);
	}
	
	/**
	 * Register a string serializer for a class.
	 * @param c
	 * @param s
	 */
	public void registerConvertibleClass(Class<?> c, StringSerializer<?> s) {
		registeredConvertibleClasses.put(c, s);
	}
	
}
